/// ===============================================================================
/// Copyright (C) 2011  Ron Cresswell
/// ===============================================================================
/// This program is free software; you can redistribute it and/or
/// modify it under the terms of the GNU General Public License
/// as published by the Free Software Foundation; either version 2
/// of the License, or (at your option) any later version.
/// 
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
/// GNU General Public License for more details.
/// 
/// You should have received a copy of the GNU General Public License
/// along with this program; if not, write to the Free Software
/// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
/// ===============================================================================
/// 
/// Project:        SharePoint Filtered Search
/// Author:         Ron Cresswell (ron.cresswell@angrymansoftware.com.au)
/// Company:        Angry Man Software
/// Date:           18/07/2011 (18 July 2011)  
/// Version:        0.97 (Beta)
///
/// ===============================================================================
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Text;

namespace AngryManSoftware.WebParts
{
    class AMStyle
    {
        /*
         * Put color methods in here too - take a color and produce 3 shades of it from light to dark
         * Use these in the style sheets.
         * 
         * So the style determines the border layout and the color determines, well, the colour.
         * 
         * Take the given colour as being the main colour. Calculate a dark version for the borders
         * and a light version for the headers.
         */
        Color lightColor;
        Color darkColor;
        Color mainColor;
        private string _message;
        public string Message
        {
            get { return _message; }
            set { _message = value; }
        }
        public AMStyle(Color c)
        {
            _message = "";
            mainColor = c;
            double h;
            double s;
            double v;
            //double h = mainColor.GetHue(); // 0-360
            //double s = mainColor.GetSaturation(); // 0-1
            //double v = mainColor.GetBrightness(); // 0-1
            rgb2hsv((int)c.R, (int)c.G, (int)c.B, out h, out s, out v);
            int r = 0;
            int g = 0;
            int b = 0;
            double vf = 1.0 - v; // diff between brightness and white
            double sf = 1.0 - s; // diff between saturation and fully saturated
            double ns = s * 0.5; // half saturation
            double nv = v + 0.75 * vf; // another 3/4 the way towards white
            hsv2rgb(h, ns, nv, out r, out g, out b);
            lightColor = Color.FromArgb(r, g, b);
            ns = s + sf * 0.5; // half way towards fully saturated
            nv = v * 0.5; // half brightness
            hsv2rgb(h, ns, nv, out r, out g, out b);
            darkColor = Color.FromArgb(r, g, b);
        }

        public string getStyles()
        {
            string main_color = ColorTranslator.ToHtml(mainColor);
            string light_color = ColorTranslator.ToHtml(lightColor);
            string dark_color = ColorTranslator.ToHtml(darkColor);
            string styleBlock = string.Empty;
            string fString = "table.amstyle{0} {{ background-color:{1};border-color:{2};border-width:{3};border-style:solid; border-collapse:{4}; }}";
            string lbString = "select.amlbstyle{0} {{ background-color:{1}; border-bottom-style:{2}; border-bottom-color:{3}; border-bottom-width:{4}; border-collapse:{5}; }}";
            
            // args are: style name, bg-col, border-col, border-wid, border-collapse.
            //
            // Note: Border attribute doesn't seem to work on select options (i.e. items)
            // - or at least it works inconsistently.
            //
            string style = "<style type=\"text/css\"> "
                + string.Format(fString, "1", main_color, dark_color, "1px", "collapse")
                + string.Format(fString, "2", main_color, dark_color, "1px", "collapse")
                + string.Format(fString, "3", main_color, dark_color, "0px", "collapse")
                + string.Format(fString, "1 th", main_color, dark_color, "0px 0px 1px 0px", "collapse")
                + string.Format(fString, "2 th", main_color, dark_color, "1px", "collapse")
                + string.Format(fString, "3 th", main_color, dark_color, "0px 0px 1px 0px", "separate")
                + string.Format(fString, "1 td", light_color, dark_color, "0px", "collapse")
                + string.Format(fString, "2 td", light_color, dark_color, "0px", "collapse")
                + string.Format(fString, "3 td", light_color, dark_color, "0px", "collapse")
                + string.Format(lbString, "1", light_color, "none", dark_color, "thin", "separate")
                + string.Format(lbString, "2", light_color, "none", dark_color, "thin", "separate")
                + string.Format(lbString, "3", light_color, "none", dark_color, "thin", "separate")
                + string.Format(lbString, "1 option", light_color, "solid", dark_color, "thin", "separate")
                + string.Format(lbString, "2 option", light_color, "solid", main_color, "thick", "separate")
                + string.Format(lbString, "3 option", light_color, "solid", dark_color, "thick", "separate")
            + "</style>";
            return style;
        }

        private void rgb2hsv(int ri, int gi, int bi, out double h, out double s, out double v)
        {
            /*
             * Code borrowed from article on msdn (http://msdn.microsoft.com/en-us/magazine/cc164879.aspx)
             * as Color.Get[Hue,Saturation,Brightness] appears to use the HSL formulation rather than HSV
             */

            double min;
            double max;
            double delta;

            double r = (double)ri / 255;
            double g = (double)gi / 255;
            double b = (double)bi / 255;

            min = Math.Min(Math.Min(r, g), b);
            max = Math.Max(Math.Max(r, g), b);
            v = max;
            delta = max - min;
            if (max == 0 || delta == 0)
            {
                // R, G, and B must be 0, or all the same.
                // In this case, S is 0, and H is undefined.
                // Using H = 0 is as good as any...
                s = 0;
                h = 0;
            }
            else
            {
                s = delta / max;
                if (r == max)
                {
                    // Between Yellow and Magenta
                    h = (g - b) / delta;
                }
                else if (g == max)
                {
                    // Between Cyan and Yellow
                    h = 2 + (b - r) / delta;
                }
                else
                {
                    // Between Magenta and Cyan
                    h = 4 + (r - g) / delta;
                }

            }
            // Scale h to be between 0 and 360. 
            // This may require adding 360, if the value
            // is negative.
            h *= 60;
            if (h < 0)
            {
                h += 360;
            }
        }

        private void hsv2rgb(double h, double s, double v, out int r, out int g, out int b)
        {
            /*
             * Based on algorithm on web by T. Nathan Mundhenk (mundhenk@usc.edu)
             * (http://ilab.usc.edu/wiki/index.php/HSV_And_H2SV_Color_Space)
             * 
             */
            if (v < (1.0 / 255.0)) // assuming 0-255 is our available precision, this is zero
            {
                r = 0;
                g = 0;
                b = 0;
            }
            else if (s < (1.0 / 255.0)) // 
            {
                r = Convert.ToInt32((v * 255.0));
                g = Convert.ToInt32((v * 255.0));
                b = Convert.ToInt32((v * 255.0));
            }
            else
            {
                double hf = h / 60.0f;
                double tween = Math.Floor(hf);
                _message = "";
                int i = Convert.ToInt32(Math.Floor(hf));
                double f = hf - (float)i;
                double pv = v * (1 - s);
                double qv = v * (1 - s * f);
                double tv = v * (1 - s * (1 - f));
                switch (i)
                {
                    case 0:
                        r = Convert.ToInt32((255.0 * v));
                        g = Convert.ToInt32((255.0 * tv));
                        b = Convert.ToInt32((255.0 * pv));
                        break;
                    case 1:
                        r = Convert.ToInt32((255.0 * qv));
                        g = Convert.ToInt32((255.0 * v));
                        b = Convert.ToInt32((255.0 * pv));
                        break;
                    case 2:
                        r = Convert.ToInt32((255.0 * pv));
                        g = Convert.ToInt32((255.0 * v));
                        b = Convert.ToInt32((255.0 * tv));
                        break;
                    case 3:
                        r = Convert.ToInt32((255.0 * pv));
                        g = Convert.ToInt32((255.0 * qv));
                        b = Convert.ToInt32((255.0 * v));
                        break;
                    case 4:
                        r = Convert.ToInt32((255.0 * tv));
                        g = Convert.ToInt32((255.0 * pv));
                        b = Convert.ToInt32((255.0 * v));
                        break;
                    case 5:
                        r = Convert.ToInt32((255.0 * v));
                        g = Convert.ToInt32((255.0 * pv));
                        b = Convert.ToInt32((255.0 * qv));
                        break;
                    case 6:
                        r = Convert.ToInt32((255.0 * v));
                        g = Convert.ToInt32((255.0 * tv));
                        b = Convert.ToInt32((255.0 * pv));
                        break;
                    case -1:
                        r = Convert.ToInt32((255.0 * v));
                        g = Convert.ToInt32((255.0 * pv));
                        b = Convert.ToInt32((255.0 * qv));
                        break;
                    default:
                        throw new Exception("Error in converting HSV to RGB");
                }
            }
        }
    }
}
